函数重构
Extract Method(提炼方法)
problem:有一个可以组合在一起的代码片段。一大串代码,将想要注释的地方提炼成新的方法。
solution:将想要注释的地方提炼成新的方法,并且给一个好的命名,这点很重要。
1 | void printOwing(double amount) { |
重构原因?
- 函数的粒度更小,被复用的几率更大。
- 命名好的函数,可以通过读名字就知道函数的作用,提高阅读效率。
Inline Method(内联方法)
problem:当方法体比方法本身更明显时,请使用此技术。
solution:在使用临时变量的地方,直接用给这个变量赋值的表达式代替。
1 | int getRating() { |
重构原因?
并不是所有的语句都需要Extract Method,已经足够简单的不需要。
Inline Temp(内联变量)
problem:有一个临时变量,它分配了一个简单表达式的结果,仅此而已。
solution:用表达式本身替换对变量的引用。
1 | boolean hasDiscount(Order order) { |
重构原因?
- 并不是说所有的表达式都需要使用Extract Variable,已经足够简单的不需要。
- 减少局部变量,从而减少对其他重构的限制。
我的理解是通过调用方法来获取值,解释为查询。Inline Temp不是必须的,它的原因是使用它的前提。
Replace Temp with Query(以查询代替临时变量)
problem:将表达式的结果放在局部变量中,以便以后在代码中使用。
solution:将整个表达式移动到单独的方法并从中返回结果。查询方法而不是使用变量。如有必要,将新方法合并到其他方法中。
1 | double basePrice = _quantity * _itemPrice; |
重构原因?
- 以后变量的赋值逻辑可能会在多个不同方法中使用
- 如果想要使用Extract Method重构时,减少局部变量的限制,希望临时变量能在原来的方法和新方法中使用,这时候可以使用Replace Temp with Query。
变量要求只被赋值一次?
因为如果被赋值多次,那么“提取右侧表达式”时应该选择哪一个呢?
这时候就需要采用其他的重构手法来解决了。
代码清晰和性能的抉择?
使用Replace Temp with Query会导致多次调用赋值表达式的逻辑,这会造成性能上的一个损失,但是一般不需要从性能上考虑,真的出现性能问题再反向处理也是十分容易的。
Introduce Explaining Variable(引入解释性变量)
problem:有一个很难理解的表达。
solution:将表达式或其部分的结果放在不言自明的单独变量中。
1 | if ( (platform.toUpperCase().indexOf("MAC") > -1) && |
将一个复杂表达式的结果放进一个临时变量中,给一个好的命名以解释。
重构原因?
- 使用变量名可以给复杂的表达式一个基础的解释,提高代码的可读性。
- 当使用Extract Method比较麻烦的时候。
Split Temporary Variable(剖解临时变量)
problem:您有一个局部变量,用于在方法中存储各种中间值(循环变量除外)。
solution:对不同的值使用不同的变量。每个变量应该只负责一个特定的事情。
1 | double temp = 2 * (_height + _width); |
什么是循环变量和结果收集变量?
循环变量:记录循环的周期的变量i
结果收集变量:用于累加、字符串接合、写入stream或者向群集(collection)添加元素
重构原因?
不是循环变量和结果收集变量,被赋值多次,承担了多个职责,令代码阅读者糊涂。
Remove Assignments to Parameters(移除对参数的赋值动作)
不要对参数进行赋值动作,以一个临时变量取代该参数的位置。
1 | int discount (int inputVal, int quantity, int yearToDate) { |
重构原因?
降低了代码的清晰度,而且混淆了pass by value(传值〕和pass by reference (传址)这两种参数传递方式。Java只采用pass by value传递方式。
在pass by value情况下,对参数的任何修改,都不会对调用端造成任何影响。那些用过pass by reference的人可能会在这一点上犯糊涂。
另一个让人糊涂的地方是函数本体内。如果你只以参数表示【被传递进来的东西】,那么代码会清晰得多,因为这种用法在所有语言中都表现出相同语义。
什么是pass by value?
含义是使它引用(参考、指涉、指向)另一个对象。
改变对象参数的内容是没有什么问题的。
Replace Method with Method Object(以函数对象取代函数)
problem:你有一个很长的方法,局部变量是如此交织在一起,你不能应用Extract Method。
solution:将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型函数分解为数个小型函数。
1 | class Order { |
为什么需要这么做?
如果一个函数之中局部变量泛滥成灾, 那么想分解这个函数是非常困难的。Replace Temp with Query 可以助你减轻这一负担,但有时候你会发现根本无法拆解一个需要拆解的函数。
如何操作?
- 建立一个新class,根据「待被处理之函数」的用途,为这个class命名。
- 在新class中建立一个final值域,用以保存原先大型函数所驻对象。我们将这个值域称为extract class「源对象」。同时,针对原(旧)函数的每个临时变量和每个参 数,在新中建立一个个对应的值域保存之。
- 在新class中建立一个构造函数(constructor),接收源对象及原函数的所有参数作为参数。
- 在新class中建立一个compute()函数。
- 将原(旧)函数的代码拷贝到compute()函数中。如果需要调用源对象的任何函数,请以「源对象」值域调用。
- 编译。
- 将旧函数的函数本体替换为这样一条语句:「创建上述新的一个新对象, 而后调用其中的compute()函数」。
Substitute Algorithm(替换你的算法)
把某个算法替换为另一个更清晰的算法。
1 | String foundPerson(String[] people){ |